/* Copyright (c) 2007-2008, Jeffrey R Griffin
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*     * Redistributions of source code must retain the above copyright
*       notice, this list of conditions and the following disclaimer.
*     * Redistributions in binary form must reproduce the above copyright
*       notice, this list of conditions and the following disclaimer in the
*       documentation and/or other materials provided with the distribution.
*     * Neither the name of Jeffrey R Griffin nor the
*       names of his contributors may be used to endorse or promote products
*       derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY Jeffrey R Griffin ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL <copyright holder> BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

package net.visibleblue.android.zxing;

import java.lang.reflect.Field;
import java.net.URISyntaxException;

import net.visibleblue.android.serialintents.SerialIntents;
import net.visibleblue.android.zxing.result.AddressBookAUParsedResult;
import net.visibleblue.android.zxing.result.AddressBookDoCoMoParsedResult;
import net.visibleblue.android.zxing.result.AndroidIntentParsedResult;
import net.visibleblue.android.zxing.result.BookmarkDoCoMoParsedResult;
import net.visibleblue.android.zxing.result.EmailAddressParsedResult;
import net.visibleblue.android.zxing.result.EmailDoCoMoParsedResult;
import net.visibleblue.android.zxing.result.GeoParsedResult;
import net.visibleblue.android.zxing.result.ParsedReaderResult;
import net.visibleblue.android.zxing.result.ParsedReaderResultType;
import net.visibleblue.android.zxing.result.SeriesParsedResult;
import net.visibleblue.android.zxing.result.TelParsedResult;
import net.visibleblue.android.zxing.result.UPCParsedResult;
import net.visibleblue.android.zxing.result.URIParsedResult;
import net.visibleblue.android.zxing.result.URLTOParsedResult;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.Resources;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.net.ContentURI;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.provider.Contacts;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.Gallery;
import android.widget.ImageView;

import com.google.zxing.MonochromeBitmapSource;
import com.google.zxing.MultiFormatReader;
import com.google.zxing.ReaderException;
import com.google.zxing.Result;

public class ZXingClient extends Activity implements Runnable{
	public class ImageAdapter extends BaseAdapter {
		public ImageAdapter(Context c) {
			mContext = c;
		}

		public int getCount() {
			return mImageIds.length;
		}

		public Object getItem(int position) {
			return position;
		}

		public long getItemId(int position) {
			return position;
		}

		public View getView(int position, View convertView, ViewGroup parent) {
			ImageView i = new ImageView(mContext);

			i.setImageResource(mImageIds[position]);
			i.setScaleType(ImageView.ScaleType.FIT_XY);
			i.setLayoutParams(new Gallery.LayoutParams(100, 100));
			return i;
		}

		public float getAlpha(boolean focused, int offset) {
			return Math.max(0, 1.0f - (0.2f * Math.abs(offset)));
		}

		public float getScale(boolean focused, int offset) {
			return Math.max(0, 1.0f - (0.2f * Math.abs(offset)));
		}

		private Context mContext;

		private Integer[] mImageIds = {
				R.drawable.sample_1,
				R.drawable.sample_2,
				R.drawable.sample_3
		};
	}
	private final Handler _messageHandler = new Handler() {
		@Override
		public void handleMessage(Message message) {
			_progress.dismiss();
			switch (message.what) {
			case R.id.decoding_succeeded_message:
				handleDecode((Result) message.obj);
				ZXingClient.this.finish();
				break;
			case R.id.decoding_failed_message:
				Context context = getApplication();
				showAlert(context.getString(R.string.title_no_barcode_detected),
						context.getString(R.string.msg_no_barcode_detected),
						context.getString(R.string.button_ok), null, true, null);
				break;
			}
		}
	};
	private static final int ZXING_NOTIFICATIONS = 0x0002f120;
	private static Resources _res = null;
	private Bitmap _selectedBitmap = null;
	private ProgressDialog _progress = null;

	/** Called when the activity is first created. */
	@Override
	public void onCreate(Bundle icicle) {
		super.onCreate(icicle);
		_res = getResources();
		setContentView(R.layout.main);

		Gallery g = (Gallery) findViewById(R.id.gallery);
		g.setAdapter(new ImageAdapter(this));
		g.setSelectorSkin(getResources().getDrawable(R.drawable.gallery_background_1));

		g.setOnItemClickListener(new AdapterView.OnItemClickListener()
		{
			public void onItemClick(AdapterView parent, View v, int position, long id)
			{
				BitmapDrawable bd = (BitmapDrawable)((ImageView)v).getDrawable();
				try
				{
					_progress = ProgressDialog.show(ZXingClient.this, "Decoding...", "", true);
					//reflection required because the m3 sdk does not provide public access to
					//the bitmap object
					Field field = BitmapDrawable.class.getDeclaredField("mBitmap");
					field.setAccessible(true);
					_selectedBitmap = (Bitmap)field.get(bd);
					new Thread(ZXingClient.this).start();
				}
				catch(NoSuchFieldException e){}
				catch(IllegalAccessException e){}
			}

		});
	}

	@Override
	public void run() {
		Result rawResult = null;
		try {
			MonochromeBitmapSource source = new RGBMonochromeBitmapSource(_selectedBitmap);
			rawResult = new MultiFormatReader().decode(source);
		} catch (ReaderException e) {
			Message message = Message.obtain(_messageHandler, R.id.decoding_failed_message);
			message.sendToTarget();
			return;
		}
		Message message = Message.obtain(_messageHandler, R.id.decoding_succeeded_message, rawResult);
		message.sendToTarget();
	}

	private void handleDecode(Result rawResult) {
		ParsedReaderResult readerResult = ParsedReaderResult.parseReaderResult(rawResult);
		lauchSerialIntentService(buildSeries(readerResult));
	}
	
	private final void lauchSerialIntentService(Intent[] info)
	{
		Intent serviceIntent = new Intent();
		serviceIntent.setComponent(
				new ComponentName("net.visibleblue.android.serialintents",
				"net.visibleblue.android.serialintents.SerialIntents"));
		Bundle args = new Bundle();
		//ParcelableArray parr = new ParcelableArray(info);
		//args.putParcelable(SerialIntents.INPUT_INTENTS, parr);
		//SerialIntents.storeIntentInfoArray(info, args);
		//args.putParcelable(SerialIntents.INPUT_INTENTS, new ParcelableArray(info));
		SerialIntents.storeIntentArray(info, args);
		args.putInteger(SerialIntents.NOTIFICATION_ID, new Integer(ZXING_NOTIFICATIONS));
		startService(serviceIntent, args);
	}

	private static final Intent[] buildSeries(ParsedReaderResult result)
	{
		if(result.getType()==ParsedReaderResultType.SERIES)
		{
			ParsedReaderResult[] results = ((SeriesParsedResult)result).getResults();
			Intent[] series = new Intent[results.length];
			for(int i=0; i<results.length; i++)
			{
				series[i] = buildIntentInfo(results[i]);
			}
			return series;
		}
		else
		{
			return new Intent[]{buildIntentInfo(result)};
		}
	}

	//includes code from com.google.zxing.client.android.ResultHandler 
	//Copyright (C) 2008 Google Inc.
	private static final Intent buildIntentInfo(ParsedReaderResult result)
	{
		Intent intent = null;
		ParsedReaderResultType type = result.getType();
		if (type.equals(ParsedReaderResultType.ADDRESSBOOK)) {
			AddressBookDoCoMoParsedResult addressResult = (AddressBookDoCoMoParsedResult) result;
			intent = new Intent(Contacts.Intents.Insert.ACTION, Contacts.People.CONTENT_URI);
			putExtra(intent, Contacts.Intents.Insert.NAME, addressResult.getName());
			putExtra(intent, Contacts.Intents.Insert.PHONE, addressResult.getPhoneNumbers());
			putExtra(intent, Contacts.Intents.Insert.EMAIL, addressResult.getEmail());
			putExtra(intent, Contacts.Intents.Insert.NOTES, addressResult.getNote());
			putExtra(intent, Contacts.Intents.Insert.POSTAL, addressResult.getAddress());
		} else if (type.equals(ParsedReaderResultType.ADDRESSBOOK_AU)) {
			AddressBookAUParsedResult addressResult = (AddressBookAUParsedResult) result;
			intent = new Intent(Contacts.Intents.Insert.ACTION, Contacts.People.CONTENT_URI);
			putExtra(intent, Contacts.Intents.Insert.NAME, addressResult.getNames());
			putExtra(intent, Contacts.Intents.Insert.PHONE, addressResult.getPhoneNumbers());
			putExtra(intent, Contacts.Intents.Insert.EMAIL, addressResult.getEmails());
			putExtra(intent, Contacts.Intents.Insert.NOTES, addressResult.getNote());
			putExtra(intent, Contacts.Intents.Insert.POSTAL, addressResult.getAddress());
		} else if (type.equals(ParsedReaderResultType.BOOKMARK)) {
			// For now, we can only open the browser, and not actually add a bookmark
			try
			{
				intent = new Intent(Intent.VIEW_ACTION, new ContentURI(((BookmarkDoCoMoParsedResult) result).getURI()));
			} catch (URISyntaxException e) {
				return null;
			}
		} else if (type.equals(ParsedReaderResultType.URLTO)) {
			try
			{
				intent = new Intent(Intent.VIEW_ACTION, new ContentURI(((URLTOParsedResult) result).getURI()));
			} catch (URISyntaxException e) {
				return null;
			}
		} else if (type.equals(ParsedReaderResultType.EMAIL)) {
			EmailDoCoMoParsedResult emailResult = (EmailDoCoMoParsedResult) result;
			try
			{
				intent = new Intent(Intent.SENDTO_ACTION, new ContentURI(emailResult.getTo()));
			} catch (URISyntaxException e) {
				return null;
			}
			putExtra(intent, "subject", emailResult.getSubject());
			putExtra(intent, "body", emailResult.getBody());
		} else if (type.equals(ParsedReaderResultType.EMAIL_ADDRESS)) {
			EmailAddressParsedResult emailResult = (EmailAddressParsedResult) result;
			try
			{
				intent = new Intent(Intent.SENDTO_ACTION, new ContentURI(emailResult.getEmailAddress()));
			} catch (URISyntaxException e) {
				return null;
			}
		} else if (type.equals(ParsedReaderResultType.TEL)) {
			TelParsedResult telResult = (TelParsedResult) result;
			try
			{
				intent = new Intent(Intent.DIAL_ACTION, new ContentURI("tel:" + telResult.getNumber()));
			} catch (URISyntaxException e) {
				return null;
			}
		} else if (type.equals(ParsedReaderResultType.GEO)) {
			GeoParsedResult geoResult = (GeoParsedResult) result;
			try
			{
				intent = new Intent(Intent.VIEW_ACTION, new ContentURI(geoResult.getGeoURI()));
			} catch (URISyntaxException e) {
				return null;
			}
		} else if (type.equals(ParsedReaderResultType.UPC)) {
			UPCParsedResult upcResult = (UPCParsedResult) result;
			try
			{
				ContentURI uri = new ContentURI("http://www.upcdatabase.com/item.asp?upc=" + upcResult.getUPC());
				intent = new Intent(Intent.VIEW_ACTION, uri);
			} catch (URISyntaxException e) {
				return null;
			}
		} else if (type.equals(ParsedReaderResultType.URI)) {
			URIParsedResult uriResult = (URIParsedResult) result;
			try
			{
				intent = new Intent(Intent.VIEW_ACTION, new ContentURI(uriResult.getURI()));
			} catch (URISyntaxException e) {
				return null;
			}
		} else if (type.equals(ParsedReaderResultType.ANDROID_INTENT)) {
			intent = ((AndroidIntentParsedResult) result).getIntent();
		}
		return intent;
	}

	private static void putExtra(Intent intent, String key, String value) {
		if (value != null && value.length() > 0) {
			intent.putExtra(key, value);
		}
	}

	private static void putExtra(Intent intent, String key, String[] value) {
		if (value != null && value.length > 0) {
			putExtra(intent, key, value[0]);
		}
	}
}